package com.hyl.gulimall.order.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.annotation.PostConstruct;

/**
 * @author hyl_marco
 * @data 2022/5/18 - 15:38
 * TODO:这里为什么会出现循环依赖的问题？
 * 将messageconvertor新建一个配置类进行配置
 */
@Configuration
public class RabbitConfig {


//    @Autowired
    RabbitTemplate rabbitTemplate;

    @Primary
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setMessageConverter(messageConverter());
        initRabbitTemplate();
        return rabbitTemplate;
    }

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate,实现消息的事务
     * @PostConstruct 注解指在该config对象创建完成之后再执行这个方法
     * 1. 服务收到消息就回调
     *      i. spring.rabbitmq.publisher-confirms=true
     *      ii. 设置确认回调 ConfirmCallback
     * 2. 消息正确抵达队列进行回调
     *      #消息到达队列就的确认 spring.rabbitmq.publisher-returns=true
     *      #到达队列，以异步的方式优先回调 spring.rabbitmq.template.mandatory=true
     * 3. 消费端确认（只有确认抵达了消费端，才能删除该消息）
     *      i. 默认是自动确认的，只要消息接受到，客户端会自动确认，服务端会移除这个消息
     *          问题：如果收到很多消息，还没处理完消费者宕机了，消息丢失
     *          解决：手动进行确认,只要没有告诉MQ确认，就一直是unacked
     *          spring.rabbitmq.listener.simple.acknowledge-mode=manual
     *          channel.basicAck(deliveryTag，false)签收：业务成功
     *          channel.basicNack()拒签：业务失败
     *
     */
//    @PostConstruct
    public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {

            /**
             * 只要消息抵达服务器，就算成功
             * @param correlationData 当前消息唯一关联的数据（位移id）
             * @param b 消息是否收到
             * @param s 失败原因
             *          消息丢失
             * 1.做好消息确认机制（这里是publisher，consumer通过手动ack保证）
             * 2.每一个发送的消息在数据库做好记录，定期将失败的进行重发
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                //服务器收到的消息
                System.out.println("confirm...corelationData[" + correlationData + "]==>ack[" + b + "]==>cause[" + s + "]");
            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息没有投递给指定的队列，就触发这个失败回调
             * @param message 投递失败的消息
             * @param i 回复的状态码
             * @param s 回复的文本内容
             * @param s1 该消息的交换机
             * @param s2 该消息的路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                //报错误了，修改数据库当前消息的状态→错误
                System.out.println("Fail...Message[" + message + "]==>code[" + i + "]==>text[" + s + "]==>exchange["+s1+"]==>routeKey["+s2+"]");
            }
        });
    }
}
